package com.lx.constants.aspect;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONUtil;
import com.lx.constants.aop.SysLog;
import com.lx.core.spring.RunAfterStart;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * Created by lanyxp on 2020/7/14
 */
@Slf4j
@Component
public class SysLogAspect implements RunAfterStart, DisposableBean {
    private LinkedBlockingQueue queue;
    private ThreadPoolExecutor executorService;
    private int currThreadNum;
    private volatile boolean running = true;
    private static int MAX_THREAD_NUM = 5;
    private static int MAX_QUEUE_THREAD = 200;

    @Override
    public void runInit(Object[] arges) {
        queue = new LinkedBlockingQueue(MAX_QUEUE_THREAD * (MAX_THREAD_NUM + 1));
        executorService = (ThreadPoolExecutor) Executors.newCachedThreadPool(ThreadUtil.newNamedThreadFactory("lx-", true));
        executorService.execute(new SysLogThread());
        currThreadNum = 1;
        log.debug("size:{},queueSize={},ThreadPoolExecutorSize={}", currThreadNum, queue.size(), executorService.getPoolSize());
    }

    @Override
    public void destroy() throws Exception {
        running = false;
        executorService.shutdown();
    }

    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)" + " && @annotation(sysLog)")
    public void webLogCut(SysLog sysLog) {

    }

    @Before("webLogCut(sysLog)")
    public void saveLog(JoinPoint joinPoint, SysLog sysLog) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        String method = className + methodName + "()";
        final Object[] args = joinPoint.getArgs();
        final String requestParam = JSONUtil.toJsonStr(args[0]);
        //获取相关参数 创建实体类 TODO
        final String values = sysLog.values();

        //异步执行 TODO
//        queue.offer(benas);

    }

    /**
     * 日志消费线程
     */
    private class SysLogThread implements Runnable {
        @Override
        public void run() {
            while (running) {
                try {
                    final Object take = queue.take();
                    //this.LogService.insert(take);
                    synchronized (executorService) {
                        final int queueSize = queue.size();
                        //如果队列的size超过阀值 线程数少于5个的话 增加线程去消费
                        if (currThreadNum < MAX_THREAD_NUM && queueSize > MAX_QUEUE_THREAD * (currThreadNum + 1)) {
                            currThreadNum++;
                            executorService.execute(new SysLogThread());
                            //若队列中没有数据了的话 那么将回收对于的线程 当n个线程(n>=2) 数据少于 MAX_QUEUE_THREAD * (currThreadNum - 1)则回收
                        } else if (currThreadNum > 1 && queueSize < MAX_QUEUE_THREAD * (currThreadNum - 1)) {
                            currThreadNum--;
                            break;
                        }
                        //MAX_THREAD_NUM = 5
                    }
                } catch (Exception e) {
                    log.error("执行异常失败服务系统端:{}", e);
                }
            }
        }
    }
}
